﻿using System;
using System.Configuration;
using com.sun.org.apache.xerces.@internal.jaxp;
using java.security;
using java.io;
using java.util;
using java.security.cert;
using javax.xml.parsers;
using es.mityc.javasign.pkstore;
using es.mityc.javasign.pkstore.keystore;
using es.mityc.javasign.trust;
using es.mityc.javasign.xml.xades.policy;
using es.mityc.firmaJava.libreria.xades;
using es.mityc.javasign.xml.refs;
using es.mityc.firmaJava.libreria.utilidades;
using org.w3c.dom;
using sviudes.blogspot.com;
using System.Xml;
using System.Text;
using Data;
using AutorizacionComprobantesServiceCer = FacturacionElectronica.ConsultaSriCer.AutorizacionComprobantesService;
using AutorizacionComprobantesServiceProd = FacturacionElectronica.ConsultaSriProd.AutorizacionComprobantesService;
using RecepcionComprobantesServiceCer = FacturacionElectronica.EnvioSriCer.RecepcionComprobantesService;
using RecepcionComprobantesServiceProd = FacturacionElectronica.EnvioSriProd.RecepcionComprobantesService;

namespace FacturacionElectronica
{
    public class ConsumirServiciosFacturacionElectronica
    {
        #region Variables
        public event EventHandler IniciaLoading;
        public event EventHandler FinilizaLoading;
        #endregion

        #region Disparadores de eventos
        private void OnFinilizaLoading()
        {
            if (FinilizaLoading != null)
                FinilizaLoading(this, new EventArgs());
        }

        private void OnIniciaLoading()
        {
            if (IniciaLoading != null)
                IniciaLoading(this, new EventArgs());
        }
        #endregion

        #region Metodos
        /// <summary>
        /// Metodo para consultar el estado de un comprobante tributario desde el SRI
        /// </summary>
        /// <param name="claveAcceso">Clave de acceso del docuemento tributario</param>
        /// <param name="ambiente">pruebas o produccion P prod C cert</param>
        /// <returns>AutorizacionFactura</returns>
        public AutorizacionFactura ConsultarFactura(string claveAcceso, char ambiente)
        {
            OnIniciaLoading();
            AutorizacionFactura resultado = new AutorizacionFactura();
            if (ambiente == 'P')
            {
                using (var ctx = new AutorizacionComprobantesServiceProd())
                {
                    var result = ctx.autorizacionComprobante(claveAcceso);
                    var soapRequest = TraceExtension.XmlRequest.OuterXml;
                    var soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            else if (ambiente == 'C')
            {
                using (var ctx = new AutorizacionComprobantesServiceCer())
                {
                    var result = ctx.autorizacionComprobante(claveAcceso);
                    var soapRequest = TraceExtension.XmlRequest.OuterXml;
                    var soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }

            OnFinilizaLoading();
            return resultado;
        }

        /// <summary>
        /// Metodo para enviar un comprobante tributario al SRI para su posterior aprovacion
        /// </summary>
        /// <param name="archivoArray">byte array del documento tributario firmado</param>
        /// <param name="ambiente">pruebas o produccion P prod C cert</param>
        /// <returns>AutorizacionFactura</returns>
        public AutorizacionFactura EnviarFactura(byte[] archivoArray, char ambiente)
        {
            OnIniciaLoading();
            AutorizacionFactura resultado = new AutorizacionFactura();
            if (ambiente == 'P')
            {
                using (var ctx = new RecepcionComprobantesServiceProd())
                {
                    var result = ctx.validarComprobante(archivoArray);
                    var soapRequest = TraceExtension.XmlRequest.OuterXml;
                    var soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            else if (ambiente == 'C')
            {
                using (var ctx = new RecepcionComprobantesServiceCer())
                {
                    var result = ctx.validarComprobante(archivoArray);
                    var soapRequest = TraceExtension.XmlRequest.OuterXml;
                    var soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            OnFinilizaLoading();
            return resultado;
        }

        /// <summary>
        /// Metodo para consultar el estado de un comprobante tributario desde el SRI
        /// </summary>
        /// <param name="claveAcceso">Clave de acceso del docuemento tributario</param>
        /// <param name="ambiente">pruebas o produccion P prod C cert</param>
        /// <returns>string</returns>
        public string ConsultarFacturaString(string claveAcceso, char ambiente)
        {
            OnIniciaLoading();
            string soapRequest = string.Empty;
            string soapResponse = string.Empty;
            if (ambiente == 'P')
            {
                using (var ctx = new AutorizacionComprobantesServiceProd())
                {
                    var result = ctx.autorizacionComprobante(claveAcceso);
                    soapRequest = TraceExtension.XmlRequest.OuterXml;
                    soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            else if (ambiente == 'C')
            {
                using (var ctx = new AutorizacionComprobantesServiceCer())
                {
                    var result = ctx.autorizacionComprobante(claveAcceso);
                    soapRequest = TraceExtension.XmlRequest.OuterXml;
                    soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            OnFinilizaLoading();
            return soapResponse;
        }

        /// <summary>
        /// Metodo para enviar un comprobante tributario al SRI para su posterior aprovacion
        /// </summary>
        /// <param name="archivoArray">byte array del documento tributario firmado</param>
        /// <param name="ambiente">pruebas o produccion P prod C cert</param>
        /// <returns>string</returns>
        public string EnviarFacturaString(byte[] archivoArray, char ambiente)
        {
            OnIniciaLoading();
            string soapRequest = string.Empty;
            string soapResponse = string.Empty;
            if (ambiente == 'P')
            {
                using (var ctx = new RecepcionComprobantesServiceProd())
                {
                    var result = ctx.validarComprobante(archivoArray);
                    soapRequest = TraceExtension.XmlRequest.OuterXml;
                    soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            else if (ambiente == 'C')
            {
                using (var ctx = new RecepcionComprobantesServiceCer())
                {
                    var result = ctx.validarComprobante(archivoArray);
                    soapRequest = TraceExtension.XmlRequest.OuterXml;

                    soapResponse = TraceExtension.XmlResponse.OuterXml;
                }
            }
            OnFinilizaLoading();
            return soapResponse;
        }

        /// <summary>
        /// Metodo para obtener un xml desde un byte array
        /// </summary>
        /// <param name="byteXml">byte array de archivo xml</param>
        /// <returns></returns>
        public string ObtenerXml(byte[] byteXml)
        {
            var str = System.Text.Encoding.Default.GetString(byteXml);
            //return (UtilidadFirmaElectronica.decodeUTF(byteXml)).Replace('\0', ' ');
            return str;
        }

        /// <summary>
        /// Metodod que firma un documento xml
        /// </summary>
        /// <param name="pathOrigenXml">direccion en la que se encuentra el documento xml</param>
        /// <returns></returns>
        public byte[] FirmarXml(string pathOrigenXml)
        {
            OnIniciaLoading();
            string nombreArchivo = string.Empty;
            PrivateKey privateKey;
            Provider provider;
            byte[] archivoArray = new[] { new byte() };
            byte[] array = new[] { new byte() };
            X509Certificate certificate = LoadCertificate(ConfigurationSettings.AppSettings["Certificado"], ConfigurationSettings.AppSettings["Clave"], out privateKey, out provider);
            if (certificate != null)
            {
                TrustFactory.instance = TrustExtendFactory.newInstance();
                TrustFactory.truster = MyPropsTruster.getInstance();
                PoliciesManager.POLICY_SIGN = new es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager();
                PoliciesManager.POLICY_VALIDATION = new es.mityc.javasign.xml.xades.policy.facturae.Facturae31Manager();

                DataToSign dataToSign = new DataToSign();
                dataToSign.setXadesFormat(EnumFormatoFirma.XAdES_BES);
                dataToSign.setEsquema(XAdESSchemas.XAdES_132);
                dataToSign.setPolicyKey("facturae31");
                dataToSign.setAddPolicy(true);
                dataToSign.setXMLEncoding("UTF-8");
                dataToSign.setEnveloped(true);
                dataToSign.addObject(new ObjectToSign(new AllXMLToSign(), "Descripcion del documento", null, "text/xml", null));
                dataToSign.setDocument(LoadXML(pathOrigenXml));
                Object[] res = new FirmaXML().signFile(certificate, dataToSign, privateKey, provider);
                nombreArchivo = pathOrigenXml.Replace(".xml", string.Empty) + "_signed" + DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + ".xml";
                UtilidadTratarNodo.saveDocumentToOutputStream((Document)res[0], new FileOutputStream(nombreArchivo), true);
                archivoArray = UtilidadFicheros.readFile(new java.io.File(nombreArchivo));
                var str = System.Text.Encoding.Default.GetString(archivoArray).Replace('\0', ' ');
                var dt = str.Split(new[] { "<etsi:SignaturePolicyIdentifier>", "</etsi:SignaturePolicyIdentifier>" }, StringSplitOptions.None);
                str = dt[0] + dt[2];
                array = Encoding.ASCII.GetBytes(str);
            }
            OnFinilizaLoading();
            //return archivoArray;
            return array;
        }

        /// <summary>
        /// Metodo que carga un archivo xml 
        /// </summary>
        /// <param name="path">direccion en la que se encuentra el documento xml</param>
        /// <returns></returns>
        private Document LoadXML(string path)
        {
            OnIniciaLoading();
            DocumentBuilderFactoryImpl dbfImpl = new DocumentBuilderFactoryImpl();
            DocumentBuilderFactory dbf = dbfImpl;
            dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            OnFinilizaLoading();
            return dbf.newDocumentBuilder().parse(new BufferedInputStream(new FileInputStream(path)));
        }

        /// <summary>
        /// Metodo que carga el certificado digital 
        /// </summary>
        /// <param name="path">direccion en la que se encuentra el certificado</param>
        /// <param name="password">calve de proteccion del certificado</param>
        /// <param name="privateKey">parametro de salida con el key privado del certificado</param>
        /// <param name="provider">parametro de salida con el provider del certificado</param>
        /// <returns></returns>
        private X509Certificate LoadCertificate(string path, string password, out PrivateKey privateKey, out Provider provider)
        {
            OnIniciaLoading();
            X509Certificate certificate = null;
            provider = null;
            privateKey = null;

            //Cargar certificado de fichero PFX
            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(new BufferedInputStream(new FileInputStream(path)), password.ToCharArray());
            IPKStoreManager storeManager = new KSStore(ks, new PassStoreKS(password));
            List certificates = storeManager.getSignCertificates();

            //Si encontramos el certificado...
            if (certificates.size() == 1)
            {
                certificate = (X509Certificate)certificates.get(0);

                // Obtención de la clave privada asociada al certificado
                privateKey = storeManager.getPrivateKey(certificate);

                // Obtención del provider encargado de las labores criptográficas
                provider = storeManager.getProvider(certificate);

                //return certificate;
            }
            OnFinilizaLoading();
            return certificate;
        }

        /// <summary>
        /// Da formato al arvhivo xml.
        /// </summary>
        /// <param name="inputXml">cadena xml</param>
        /// <returns></returns>
        public string FormatXml(string inputXml)
        {
            OnIniciaLoading();
            StringBuilder builder = new StringBuilder();
            try
            {
                inputXml = inputXml.Replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "");
                XmlDocument document = new XmlDocument();
                document.Load(new System.IO.StringReader(inputXml));
                using (XmlTextWriter writer = new XmlTextWriter(new System.IO.StringWriter(builder)))
                {
                    writer.Formatting = Formatting.Indented;
                    document.Save(writer);
                }
            }
            catch (Exception e)
            {
                builder.Append("El archivo XML no tiene el formato correcto.");
            }
            OnFinilizaLoading();
            return builder.ToString();
        }
        #endregion
    }
}
